/* * * Copyright 2016 Robert Winkler * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * */ package io.github.resilience4j.circuitbreaker; import io.github.resilience4j.test.HelloWorldService; import io.vavr.*; import io.vavr.control.Try; import org.junit.Before; import org.junit.Test; import org.mockito.BDDMockito; import org.mockito.Mockito; import javax.xml.ws.WebServiceException; import java.io.IOException; import java.net.SocketTimeoutException; import java.time.Duration; import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.concurrent.ExecutionException; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; import static io.vavr.API.*; import static io.vavr.API.$; import static io.vavr.Predicates.*; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; public class CircuitBreakerTest { private HelloWorldService helloWorldService; @Before public void setUp(){ helloWorldService = Mockito.mock(HelloWorldService.class); } @Test public void shouldDecorateSupplierAndReturnWithSuccess() { // Given CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("testName"); CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics(); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(0); // Given the HelloWorldService returns Hello world BDDMockito.given(helloWorldService.returnHelloWorld()).willReturn("Hello world"); //When Supplier<String> supplier = CircuitBreaker.decorateSupplier(circuitBreaker, helloWorldService::returnHelloWorld); //Then assertThat(supplier.get()).isEqualTo("Hello world"); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(1); assertThat(metrics.getNumberOfFailedCalls()).isEqualTo(0); assertThat(metrics.getNumberOfSuccessfulCalls()).isEqualTo(1); // Then the helloWorldService should be invoked 1 time BDDMockito.then(helloWorldService).should(Mockito.times(1)).returnHelloWorld(); } @Test public void shouldExecuteSupplierAndReturnWithSuccess() { // Given CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("testName"); CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics(); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(0); // Given the HelloWorldService returns Hello world BDDMockito.given(helloWorldService.returnHelloWorld()).willReturn("Hello world"); //When String result = circuitBreaker.executeSupplier(helloWorldService::returnHelloWorld); //Then assertThat(result).isEqualTo("Hello world"); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(1); assertThat(metrics.getNumberOfFailedCalls()).isEqualTo(0); assertThat(metrics.getNumberOfSuccessfulCalls()).isEqualTo(1); // Then the helloWorldService should be invoked 1 time BDDMockito.then(helloWorldService).should(Mockito.times(1)).returnHelloWorld(); } @Test public void shouldDecorateSupplierAndReturnWithException() { // Given CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("testName"); CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics(); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(0); // Given the HelloWorldService throws an exception BDDMockito.given(helloWorldService.returnHelloWorld()).willThrow(new RuntimeException("BAM!")); //When Supplier<String> supplier = CircuitBreaker.decorateSupplier(circuitBreaker, helloWorldService::returnHelloWorld); //Then Try<String> result = Try.ofSupplier(supplier); assertThat(result.isFailure()).isTrue(); assertThat(result.failed().get()).isInstanceOf(RuntimeException.class); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(1); assertThat(metrics.getNumberOfFailedCalls()).isEqualTo(1); assertThat(metrics.getNumberOfSuccessfulCalls()).isEqualTo(0); // Then the helloWorldService should be invoked 1 time BDDMockito.then(helloWorldService).should(Mockito.times(1)).returnHelloWorld(); } @Test public void shouldDecorateCheckedSupplierAndReturnWithSuccess() throws Throwable { // Given CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.ofDefaults(); CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker("testName"); CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics(); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(0); // Given the HelloWorldService returns Hello world BDDMockito.given(helloWorldService.returnHelloWorldWithException()).willReturn("Hello world"); //When CheckedFunction0<String> checkedSupplier = CircuitBreaker.decorateCheckedSupplier(circuitBreaker, helloWorldService::returnHelloWorldWithException); //Then assertThat(checkedSupplier.apply()).isEqualTo("Hello world"); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(1); assertThat(metrics.getNumberOfFailedCalls()).isEqualTo(0); assertThat(metrics.getNumberOfSuccessfulCalls()).isEqualTo(1); // Then the helloWorldService should be invoked 1 time BDDMockito.then(helloWorldService).should(Mockito.times(1)).returnHelloWorldWithException(); } @Test public void shouldDecorateCheckedSupplierAndReturnWithException() throws Throwable { // Given CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.ofDefaults(); CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker("testName"); CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics(); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(0); // Given the HelloWorldService throws an exception BDDMockito.given(helloWorldService.returnHelloWorldWithException()).willThrow(new RuntimeException("BAM!")); //When CheckedFunction0<String> checkedSupplier = CircuitBreaker.decorateCheckedSupplier(circuitBreaker, helloWorldService::returnHelloWorldWithException); //Then Try<String> result = Try.of(checkedSupplier); assertThat(result.isFailure()).isTrue(); assertThat(result.failed().get()).isInstanceOf(RuntimeException.class); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(1); assertThat(metrics.getNumberOfFailedCalls()).isEqualTo(1); assertThat(metrics.getNumberOfSuccessfulCalls()).isEqualTo(0); // Then the helloWorldService should be invoked 1 time BDDMockito.then(helloWorldService).should(Mockito.times(1)).returnHelloWorldWithException(); } @Test public void shouldDecorateCallableAndReturnWithSuccess() throws Throwable { // Given CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.ofDefaults(); CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker("testName"); CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics(); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(0); // Given the HelloWorldService returns Hello world BDDMockito.given(helloWorldService.returnHelloWorldWithException()).willReturn("Hello world"); //When Callable<String> callable = CircuitBreaker.decorateCallable(circuitBreaker, helloWorldService::returnHelloWorldWithException); //Then assertThat(callable.call()).isEqualTo("Hello world"); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(1); assertThat(metrics.getNumberOfFailedCalls()).isEqualTo(0); assertThat(metrics.getNumberOfSuccessfulCalls()).isEqualTo(1); // Then the helloWorldService should be invoked 1 time BDDMockito.then(helloWorldService).should(Mockito.times(1)).returnHelloWorldWithException(); } @Test public void shouldExecuteCallableAndReturnWithSuccess() throws Throwable { // Given CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("testName"); CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics(); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(0); // Given the HelloWorldService returns Hello world BDDMockito.given(helloWorldService.returnHelloWorldWithException()).willReturn("Hello world"); //When String result = circuitBreaker.executeCallable(helloWorldService::returnHelloWorldWithException); //Then assertThat(result).isEqualTo("Hello world"); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(1); assertThat(metrics.getNumberOfFailedCalls()).isEqualTo(0); assertThat(metrics.getNumberOfSuccessfulCalls()).isEqualTo(1); // Then the helloWorldService should be invoked 1 time BDDMockito.then(helloWorldService).should(Mockito.times(1)).returnHelloWorldWithException(); } @Test public void shouldDecorateCallableAndReturnWithException() throws Throwable { // Given CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.ofDefaults(); CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker("testName"); CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics(); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(0); // Given the HelloWorldService throws an exception BDDMockito.given(helloWorldService.returnHelloWorldWithException()).willThrow(new RuntimeException("BAM!")); //When Callable<String> callable = CircuitBreaker.decorateCallable(circuitBreaker, helloWorldService::returnHelloWorldWithException); //Then Try<String> result = Try.of(callable::call); assertThat(result.isFailure()).isTrue(); assertThat(result.failed().get()).isInstanceOf(RuntimeException.class); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(1); assertThat(metrics.getNumberOfFailedCalls()).isEqualTo(1); assertThat(metrics.getNumberOfSuccessfulCalls()).isEqualTo(0); // Then the helloWorldService should be invoked 1 time BDDMockito.then(helloWorldService).should(Mockito.times(1)).returnHelloWorldWithException(); } @Test public void shouldDecorateCheckedRunnableAndReturnWithSuccess() throws Throwable { // Given CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.ofDefaults(); CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker("testName"); CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics(); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(0); //When CheckedRunnable checkedRunnable = CircuitBreaker.decorateCheckedRunnable(circuitBreaker, helloWorldService::sayHelloWorldWithException); //Then checkedRunnable.run(); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(1); assertThat(metrics.getNumberOfFailedCalls()).isEqualTo(0); assertThat(metrics.getNumberOfSuccessfulCalls()).isEqualTo(1); // Then the helloWorldService should be invoked 1 time BDDMockito.then(helloWorldService).should(Mockito.times(1)).sayHelloWorldWithException(); } @Test public void shouldDecorateCheckedRunnableAndReturnWithException() throws Throwable { // Given CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.ofDefaults(); CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker("testName"); CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics(); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(0); //When CheckedRunnable checkedRunnable = CircuitBreaker.decorateCheckedRunnable(circuitBreaker, () -> { throw new RuntimeException("BAM!"); }); //Then Try<Void> result = Try.run(checkedRunnable); assertThat(result.isFailure()).isTrue(); assertThat(result.failed().get()).isInstanceOf(RuntimeException.class); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(1); assertThat(metrics.getNumberOfFailedCalls()).isEqualTo(1); assertThat(metrics.getNumberOfSuccessfulCalls()).isEqualTo(0); } @Test public void shouldDecorateRunnableAndReturnWithSuccess() throws Throwable { // Given CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.ofDefaults(); CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker("testName"); CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics(); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(0); //When Runnable runnable = CircuitBreaker.decorateRunnable(circuitBreaker, helloWorldService::sayHelloWorld); //Then runnable.run(); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(1); assertThat(metrics.getNumberOfFailedCalls()).isEqualTo(0); assertThat(metrics.getNumberOfSuccessfulCalls()).isEqualTo(1); // Then the helloWorldService should be invoked 1 time BDDMockito.then(helloWorldService).should(Mockito.times(1)).sayHelloWorld(); } @Test public void shouldExecuteRunnableAndReturnWithSuccess() throws Throwable { // Given CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.ofDefaults(); CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker("testName"); CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics(); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(0); //When circuitBreaker.executeRunnable(helloWorldService::sayHelloWorld); //Then assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(1); assertThat(metrics.getNumberOfFailedCalls()).isEqualTo(0); assertThat(metrics.getNumberOfSuccessfulCalls()).isEqualTo(1); // Then the helloWorldService should be invoked 1 time BDDMockito.then(helloWorldService).should(Mockito.times(1)).sayHelloWorld(); } @Test public void shouldDecorateRunnableAndReturnWithException() throws Throwable { // Given CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.ofDefaults(); CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker("testName"); CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics(); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(0); //When Runnable runnable = CircuitBreaker.decorateRunnable(circuitBreaker, () -> { throw new RuntimeException("BAM!"); }); //Then Try<Void> result = Try.run(runnable::run); assertThat(result.isFailure()).isTrue(); assertThat(result.failed().get()).isInstanceOf(RuntimeException.class); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(1); assertThat(metrics.getNumberOfFailedCalls()).isEqualTo(1); assertThat(metrics.getNumberOfSuccessfulCalls()).isEqualTo(0); } @Test public void shouldDecorateConsumerAndReturnWithSuccess() throws Throwable { // Given CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.ofDefaults(); CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker("testName"); CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics(); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(0); //When Consumer<String> consumer = CircuitBreaker.decorateConsumer(circuitBreaker, helloWorldService::sayHelloWorldWithName); //Then consumer.accept("Tom"); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(1); assertThat(metrics.getNumberOfFailedCalls()).isEqualTo(0); assertThat(metrics.getNumberOfSuccessfulCalls()).isEqualTo(1); // Then the helloWorldService should be invoked 1 time BDDMockito.then(helloWorldService).should(Mockito.times(1)).sayHelloWorldWithName("Tom"); } @Test public void shouldDecorateConsumerAndReturnWithException() throws Throwable { // Given CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.ofDefaults(); CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker("testName"); CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics(); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(0); //When Consumer<String> consumer = CircuitBreaker.decorateConsumer(circuitBreaker, (value) -> { throw new RuntimeException("BAM!"); }); //Then Try<Void> result = Try.run(() -> consumer.accept("Tom")); assertThat(result.isFailure()).isTrue(); assertThat(result.failed().get()).isInstanceOf(RuntimeException.class); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(1); assertThat(metrics.getNumberOfFailedCalls()).isEqualTo(1); assertThat(metrics.getNumberOfSuccessfulCalls()).isEqualTo(0); } @Test public void shouldDecorateCheckedConsumerAndReturnWithSuccess() throws Throwable { // Given CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.ofDefaults(); CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker("testName"); CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics(); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(0); //When CheckedConsumer<String> checkedConsumer = CircuitBreaker.decorateCheckedConsumer(circuitBreaker, helloWorldService::sayHelloWorldWithNameWithException); //Then checkedConsumer.accept("Tom"); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(1); assertThat(metrics.getNumberOfFailedCalls()).isEqualTo(0); assertThat(metrics.getNumberOfSuccessfulCalls()).isEqualTo(1); // Then the helloWorldService should be invoked 1 time BDDMockito.then(helloWorldService).should(Mockito.times(1)).sayHelloWorldWithNameWithException("Tom"); } @Test public void shouldDecorateCheckedConsumerAndReturnWithException() throws Throwable { // Given CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.ofDefaults(); CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker("testName"); CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics(); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(0); //When CheckedConsumer<String> checkedConsumer = CircuitBreaker.decorateCheckedConsumer(circuitBreaker, (value) -> { throw new RuntimeException("BAM!"); }); //Then Try<Void> result = Try.run(() -> checkedConsumer.accept("Tom")); assertThat(result.isFailure()).isTrue(); assertThat(result.failed().get()).isInstanceOf(RuntimeException.class); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(1); assertThat(metrics.getNumberOfFailedCalls()).isEqualTo(1); assertThat(metrics.getNumberOfSuccessfulCalls()).isEqualTo(0); } @Test public void shouldDecorateFunctionAndReturnWithSuccess() throws Throwable { // Given CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.ofDefaults(); CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker("testName"); CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics(); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(0); // Given the HelloWorldService returns Hello world BDDMockito.given(helloWorldService.returnHelloWorldWithName("Tom")).willReturn("Hello world Tom"); //When Function<String, String> function = CircuitBreaker.decorateFunction(circuitBreaker, helloWorldService::returnHelloWorldWithName); //Then assertThat(function.apply("Tom")).isEqualTo("Hello world Tom"); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(1); assertThat(metrics.getNumberOfFailedCalls()).isEqualTo(0); assertThat(metrics.getNumberOfSuccessfulCalls()).isEqualTo(1); // Then the helloWorldService should be invoked 1 time BDDMockito.then(helloWorldService).should(Mockito.times(1)).returnHelloWorldWithName("Tom"); } @Test public void shouldDecorateFunctionAndReturnWithException() throws Throwable { // Given CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.ofDefaults(); CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker("testName"); CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics(); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(0); // Given the HelloWorldService throws an exception BDDMockito.given(helloWorldService.returnHelloWorldWithName("Tom")).willThrow(new RuntimeException("BAM!")); //When Function<String, String> function = CircuitBreaker.decorateFunction(circuitBreaker, helloWorldService::returnHelloWorldWithName); //Then Try<String> result = Try.of(() -> function.apply("Tom")); assertThat(result.isFailure()).isTrue(); assertThat(result.failed().get()).isInstanceOf(RuntimeException.class); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(1); assertThat(metrics.getNumberOfFailedCalls()).isEqualTo(1); assertThat(metrics.getNumberOfSuccessfulCalls()).isEqualTo(0); } @Test public void shouldDecorateCheckedFunctionAndReturnWithSuccess() throws Throwable { // Given CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.ofDefaults(); CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker("testName"); CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics(); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(0); // Given the HelloWorldService returns Hello world BDDMockito.given(helloWorldService.returnHelloWorldWithNameWithException("Tom")).willReturn("Hello world Tom"); //When CheckedFunction1<String, String> function = CircuitBreaker.decorateCheckedFunction(circuitBreaker, helloWorldService::returnHelloWorldWithNameWithException); //Then assertThat(function.apply("Tom")).isEqualTo("Hello world Tom"); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(1); assertThat(metrics.getNumberOfFailedCalls()).isEqualTo(0); assertThat(metrics.getNumberOfSuccessfulCalls()).isEqualTo(1); // Then the helloWorldService should be invoked 1 time BDDMockito.then(helloWorldService).should(Mockito.times(1)).returnHelloWorldWithNameWithException("Tom"); } @Test public void shouldDecorateCheckedFunctionAndReturnWithException() throws Throwable { // Given CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.ofDefaults(); CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker("testName"); CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics(); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(0); // Given the HelloWorldService throws an exception BDDMockito.given(helloWorldService.returnHelloWorldWithNameWithException("Tom")).willThrow(new RuntimeException("BAM!")); //When CheckedFunction1<String, String> function = CircuitBreaker.decorateCheckedFunction(circuitBreaker, helloWorldService::returnHelloWorldWithNameWithException); //Then Try<String> result = Try.of(() -> function.apply("Tom")); assertThat(result.isFailure()).isTrue(); assertThat(result.failed().get()).isInstanceOf(RuntimeException.class); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(1); assertThat(metrics.getNumberOfFailedCalls()).isEqualTo(1); assertThat(metrics.getNumberOfSuccessfulCalls()).isEqualTo(0); } @Test public void shouldReturnFailureWithCircuitBreakerOpenException() { // Given // Create a custom configuration for a CircuitBreaker CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom() .ringBufferSizeInClosedState(2) .ringBufferSizeInHalfOpenState(2) .failureRateThreshold(50) .waitDurationInOpenState(Duration.ofMillis(1000)) .build(); // Create a CircuitBreakerRegistry with a custom global configuration CircuitBreaker circuitBreaker = CircuitBreaker.of("testName", circuitBreakerConfig); circuitBreaker.onError(0, new RuntimeException()); circuitBreaker.onError(0, new RuntimeException()); assertThat(circuitBreaker.getState()).isEqualTo(CircuitBreaker.State.OPEN); CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics(); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(2); assertThat(metrics.getNumberOfFailedCalls()).isEqualTo(2); //When CheckedRunnable checkedRunnable = CircuitBreaker.decorateCheckedRunnable(circuitBreaker, () -> { throw new RuntimeException("BAM!"); }); Try result = Try.run(checkedRunnable); //Then assertThat(result.isFailure()).isTrue(); assertThat(result.failed().get()).isInstanceOf(CircuitBreakerOpenException.class); } @Test public void shouldReturnFailureWithRuntimeException() { // Given CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("testName"); assertThat(circuitBreaker.getState()).isEqualTo(CircuitBreaker.State.CLOSED); CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics(); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(0); assertThat(metrics.getNumberOfFailedCalls()).isEqualTo(0); //When CheckedRunnable checkedRunnable = CircuitBreaker.decorateCheckedRunnable(circuitBreaker, () -> { throw new RuntimeException("BAM!"); }); Try result = Try.run(checkedRunnable); //Then assertThat(result.isFailure()).isTrue(); assertThat(result.failed().get()).isInstanceOf(RuntimeException.class); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(1); assertThat(metrics.getNumberOfFailedCalls()).isEqualTo(1); } @Test public void shouldNotRecordIOExceptionAsAFailure() { // tag::shouldNotRecordIOExceptionAsAFailure[] // Given CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom() .ringBufferSizeInClosedState(2) .ringBufferSizeInHalfOpenState(2) .waitDurationInOpenState(Duration.ofMillis(1000)) .recordFailure(throwable -> Match(throwable).of( Case($(instanceOf(WebServiceException.class)), true), Case($(), false))) .build(); CircuitBreaker circuitBreaker = CircuitBreaker.of("testName", circuitBreakerConfig); // Simulate a failure attempt circuitBreaker.onError(0, new WebServiceException()); // CircuitBreaker is still CLOSED, because 1 failure is allowed assertThat(circuitBreaker.getState()).isEqualTo(CircuitBreaker.State.CLOSED); //When CheckedRunnable checkedRunnable = CircuitBreaker.decorateCheckedRunnable(circuitBreaker, () -> { throw new SocketTimeoutException("BAM!"); }); Try result = Try.run(checkedRunnable); //Then assertThat(result.isFailure()).isTrue(); // CircuitBreaker is still CLOSED, because SocketTimeoutException has not been recorded as a failure assertThat(circuitBreaker.getState()).isEqualTo(CircuitBreaker.State.CLOSED); assertThat(result.failed().get()).isInstanceOf(IOException.class); // end::shouldNotRecordIOExceptionAsAFailure[] CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics(); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(1); assertThat(metrics.getNumberOfFailedCalls()).isEqualTo(1); } @Test public void shouldInvokeRecoverFunction() { // tag::shouldInvokeRecoverFunction[] // Given CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("testName"); // When I decorate my function and invoke the decorated function CheckedFunction0<String> checkedSupplier = CircuitBreaker.decorateCheckedSupplier(circuitBreaker, () -> { throw new RuntimeException("BAM!"); }); Try<String> result = Try.of(checkedSupplier) .recover(throwable -> "Hello Recovery"); // Then the function should be a success, because the exception could be recovered assertThat(result.isSuccess()).isTrue(); // and the result must match the result of the recovery function. assertThat(result.get()).isEqualTo("Hello Recovery"); // end::shouldInvokeRecoverFunction[] } @Test public void shouldInvokeMap() { // tag::shouldInvokeMap[] // Given CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("testName"); // When I decorate my function CheckedFunction0<String> decoratedSupplier = CircuitBreaker .decorateCheckedSupplier(circuitBreaker, () -> "This can be any method which returns: 'Hello"); // and chain an other function with map Try<String> result = Try.of(decoratedSupplier) .map(value -> value + " world'"); // Then the Try Monad returns a Success<String>, if all functions ran successfully. assertThat(result.isSuccess()).isTrue(); assertThat(result.get()).isEqualTo("This can be any method which returns: 'Hello world'"); // end::shouldInvokeMap[] } @Test public void shouldThrowCircuitBreakerOpenException() { // tag::shouldThrowCircuitBreakerOpenException[] // Given CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom() .ringBufferSizeInClosedState(2) .waitDurationInOpenState(Duration.ofMillis(1000)) .build(); CircuitBreaker circuitBreaker = CircuitBreaker.of("testName", circuitBreakerConfig); // Simulate a failure attempt circuitBreaker.onError(0, new RuntimeException()); // CircuitBreaker is still CLOSED, because 1 failure is allowed assertThat(circuitBreaker.getState()).isEqualTo(CircuitBreaker.State.CLOSED); // Simulate a failure attempt circuitBreaker.onError(0, new RuntimeException()); // CircuitBreaker is OPEN, because the failure rate is above 50% assertThat(circuitBreaker.getState()).isEqualTo(CircuitBreaker.State.OPEN); // When I decorate my function and invoke the decorated function Try<String> result = Try.of(CircuitBreaker.decorateCheckedSupplier(circuitBreaker, () -> "Hello")) .map(value -> value + " world"); // Then the call fails, because CircuitBreaker is OPEN assertThat(result.isFailure()).isTrue(); // Exception is CircuitBreakerOpenException assertThat(result.failed().get()).isInstanceOf(CircuitBreakerOpenException.class); // end::shouldThrowCircuitBreakerOpenException[] CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics(); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(2); assertThat(metrics.getNumberOfFailedCalls()).isEqualTo(2); } @Test public void shouldInvokeAsyncApply() throws ExecutionException, InterruptedException { // tag::shouldInvokeAsyncApply[] // Given CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("backendName"); assertThat(circuitBreaker.getState()).isEqualTo(CircuitBreaker.State.CLOSED); // When Supplier<String> decoratedSupplier = CircuitBreaker .decorateSupplier(circuitBreaker, () -> "This can be any method which returns: 'Hello"); CompletableFuture<String> future = CompletableFuture.supplyAsync(decoratedSupplier) .thenApply(value -> value + " world'"); //Then assertThat(future.get()).isEqualTo("This can be any method which returns: 'Hello world'"); // end::shouldInvokeAsyncApply[] CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics(); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(1); assertThat(metrics.getNumberOfFailedCalls()).isEqualTo(0); } @Test public void shouldDecorateCompletionStageAndReturnWithSuccess() throws ExecutionException, InterruptedException { // Given CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("backendName"); assertThat(circuitBreaker.getState()).isEqualTo(CircuitBreaker.State.CLOSED); // Given the HelloWorldService returns Hello world BDDMockito.given(helloWorldService.returnHelloWorld()).willReturn("Hello"); // When Supplier<CompletionStage<String>> completionStageSupplier = () -> CompletableFuture.supplyAsync(helloWorldService::returnHelloWorld); Supplier<CompletionStage<String>> decoratedCompletionStageSupplier = CircuitBreaker.decorateCompletionStage(circuitBreaker, completionStageSupplier); CompletionStage<String> decoratedCompletionStage = decoratedCompletionStageSupplier .get() .thenApply(value -> value + " world"); // Then the helloWorldService should be invoked 1 time assertThat(decoratedCompletionStage.toCompletableFuture().get()).isEqualTo("Hello world"); BDDMockito.then(helloWorldService).should(Mockito.times(1)).returnHelloWorld(); CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics(); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(1); assertThat(metrics.getNumberOfFailedCalls()).isEqualTo(0); } @Test public void shouldExecuteCompletionStageAndReturnWithSuccess() throws ExecutionException, InterruptedException { // Given CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("backendName"); assertThat(circuitBreaker.getState()).isEqualTo(CircuitBreaker.State.CLOSED); // Given the HelloWorldService returns Hello world BDDMockito.given(helloWorldService.returnHelloWorld()).willReturn("Hello"); // When CompletionStage<String> decoratedCompletionStage = circuitBreaker .executeCompletionStage(() -> CompletableFuture.supplyAsync(helloWorldService::returnHelloWorld)) .thenApply(value -> value + " world"); // Then the helloWorldService should be invoked 1 time assertThat(decoratedCompletionStage.toCompletableFuture().get()).isEqualTo("Hello world"); BDDMockito.then(helloWorldService).should(Mockito.times(1)).returnHelloWorld(); CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics(); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(1); assertThat(metrics.getNumberOfFailedCalls()).isEqualTo(0); } @Test public void shouldDecorateCompletionStageAndReturnWithExceptionAtSyncStage() throws ExecutionException, InterruptedException { // Given CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("backendName"); assertThat(circuitBreaker.getState()).isEqualTo(CircuitBreaker.State.CLOSED); // Given the HelloWorldService throws an exception // When Supplier<CompletionStage<String>> completionStageSupplier = () -> { throw new WebServiceException("BAM! At sync stage"); }; Supplier<CompletionStage<String>> decoratedCompletionStageSupplier = CircuitBreaker.decorateCompletionStage(circuitBreaker, completionStageSupplier); Try<CompletionStage<String>> result = Try.of(decoratedCompletionStageSupplier::get); assertThat(result.isFailure()).isEqualTo(true); assertThat(result.failed().get()).isInstanceOf(WebServiceException.class); CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics(); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(1); assertThat(metrics.getNumberOfFailedCalls()).isEqualTo(1); } @Test public void shouldDecorateCompletionStageAndReturnWithExceptionAtAsyncStage() throws ExecutionException, InterruptedException { // Given CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("backendName"); assertThat(circuitBreaker.getState()).isEqualTo(CircuitBreaker.State.CLOSED); // Given the HelloWorldService throws an exception BDDMockito.given(helloWorldService.returnHelloWorld()).willThrow(new RuntimeException("BAM! At async stage")); // When Supplier<CompletionStage<String>> completionStageSupplier = () -> CompletableFuture.supplyAsync(helloWorldService::returnHelloWorld); Supplier<CompletionStage<String>> decoratedCompletionStageSupplier = CircuitBreaker.decorateCompletionStage(circuitBreaker, completionStageSupplier); CompletionStage<String> decoratedCompletionStage = decoratedCompletionStageSupplier.get(); // Then the helloWorldService should be invoked 1 time assertThatThrownBy(decoratedCompletionStage.toCompletableFuture()::get) .isInstanceOf(ExecutionException.class).hasCause(new RuntimeException("BAM! At async stage")); BDDMockito.then(helloWorldService).should(Mockito.times(1)).returnHelloWorld(); CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics(); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(1); assertThat(metrics.getNumberOfFailedCalls()).isEqualTo(1); } @Test public void shouldChainDecoratedFunctions() throws ExecutionException, InterruptedException { // tag::shouldChainDecoratedFunctions[] // Given CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("testName"); CircuitBreaker anotherCircuitBreaker = CircuitBreaker.ofDefaults("anotherTestName"); // When I create a Supplier and a Function which are decorated by different CircuitBreakers CheckedFunction0<String> decoratedSupplier = CircuitBreaker .decorateCheckedSupplier(circuitBreaker, () -> "Hello"); CheckedFunction1<String, String> decoratedFunction = CircuitBreaker .decorateCheckedFunction(anotherCircuitBreaker, (input) -> input + " world"); // and I chain a function with map Try<String> result = Try.of(decoratedSupplier) .mapTry(decoratedFunction::apply); // Then assertThat(result.isSuccess()).isTrue(); assertThat(result.get()).isEqualTo("Hello world"); // end::shouldChainDecoratedFunctions[] CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics(); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(1); assertThat(metrics.getNumberOfFailedCalls()).isEqualTo(0); metrics = anotherCircuitBreaker.getMetrics(); assertThat(metrics.getNumberOfBufferedCalls()).isEqualTo(1); assertThat(metrics.getNumberOfFailedCalls()).isEqualTo(0); } }